package com.ys.openapi.utils;
import com.alibaba.fastjson.JSON;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import lombok.extern.slf4j.Slf4j;
import okhttp3.*;
import okhttp3.internal.Util;
import org.apache.commons.collections4.MapUtils;

import javax.net.ssl.*;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.time.Duration;
import java.util.Collections;
import java.util.Map;
import java.util.Objects;

/**
 * 云枢openapi工具类
 */
@Slf4j
public class Client {

    private final Gson gson = new GsonBuilder().setDateFormat("yyyy-MM-dd HH:mm:ss").create();

    private OkHttpClient client;

    public Client(boolean https) {
        this(5000, 8000, 8000, https);
    }

    public Client(int readTimeout, int connectTimeout, int writeTimeout, boolean https) {
        SSLContext sslContext = null;
        HostnameVerifier hostnameVerifier = null;
        ConnectionSpec connectionSpec = null;
        X509TrustManager trustAll = null;
        if (https) {
            trustAll = new X509TrustManager() {
                @Override
                public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
                }

                @Override
                public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
                }

                @Override
                public X509Certificate[] getAcceptedIssuers() {
                    return new X509Certificate[]{};
                }
            };
            TrustManager[] trustManagers = new TrustManager[]{trustAll};
            hostnameVerifier = (s, sslSession) -> s.equalsIgnoreCase(sslSession.getPeerHost());
            try {
                sslContext = SSLContext.getInstance("TLSv1.2");
                sslContext.init(null, trustManagers, new SecureRandom());
            } catch (NoSuchAlgorithmException | KeyManagementException e) {

            }
            connectionSpec = new ConnectionSpec.Builder(ConnectionSpec.MODERN_TLS)
                    .tlsVersions(TlsVersion.TLS_1_2)
                    .cipherSuites(
                            CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
                            CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
                            CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
                            CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
                            CipherSuite.TLS_DHE_RSA_WITH_AES_128_GCM_SHA256,
                            CipherSuite.TLS_DHE_RSA_WITH_AES_256_GCM_SHA384)
                    .build();
        }

        OkHttpClient.Builder builder = new OkHttpClient.Builder();
        builder.readTimeout(Duration.ofMillis(readTimeout)).connectTimeout(Duration.ofMillis(connectTimeout))
                .writeTimeout(Duration.ofMillis(writeTimeout));
        if (connectionSpec != null) {
            builder.connectionSpecs(Collections.singletonList(connectionSpec));
        }
        if (sslContext != null) {
            builder.sslSocketFactory(sslContext.getSocketFactory(), trustAll);
        }
        if (hostnameVerifier != null) {
            builder.hostnameVerifier(hostnameVerifier);
        }
        client = builder.build();
    }

    public String get(String url, Map<String, String> queryParams) throws IOException {
        return Objects.requireNonNull(client.newCall(new Request.Builder().url(buildUrl(url, queryParams)).build()).execute().body()).string();
    }

    public String get(String url, Headers headers, Map<String, String> queryParams) throws IOException {
        return Objects.requireNonNull(client.newCall(new Request.Builder().url(buildUrl(url, queryParams)).headers(headers).build()).execute().body()).string();
    }

    public <T> T getForObject(String url, Map<String, String> headers, Map<String, String> queryParams, Class<T> clazz) throws IOException {
        return json2Object(headers == null ? get(url, queryParams) : get(url, Headers.of(headers), queryParams), clazz);
    }

    public <T> T getForObject(String url, Headers headers, Map<String, String> queryParams, Class<T> clazz) throws IOException {
        return json2Object(get(url, headers, queryParams), clazz);
    }

    public String post(String url, Map<String, String> headers, Map<String, String> queryParams, Object body) throws IOException {
        return post(url, headers == null ? null : Headers.of(headers), queryParams, body);
    }

    public String post(String url, Headers headers, Map<String, String> queryParams, Object body) throws IOException {
        Request.Builder builder = new Request.Builder().url(buildUrl(url, queryParams));
        if (headers != null) {
            builder.headers(headers);
        }
        if (body != null) {
            builder.post(RequestBody.create(gson.toJson(body), MediaType.get("application/json")));
        } else {
            builder.post(Util.EMPTY_REQUEST);
        }
        Response response = client.newCall(builder.build()).execute();
        ResponseBody responseBody = response.body();
        if (responseBody != null) {
            return responseBody.string();
        }
        return null;
    }

    public <T> T postForObject(String url, Map<String, String> headers, Map<String, String> queryParams, Object body, Class<T> clazz) throws IOException {
        return json2Object(post(url, headers, queryParams, body), clazz);
    }

    public <T> T postForObject(String url, Headers headers, Map<String, String> queryParams, Object body, Class<T> clazz) throws IOException {
        return json2Object(post(url, headers, queryParams, body), clazz);
    }

    private <T> T json2Object(String json, Class<T> clazz) {
        if (json != null) {
            return gson.fromJson(json, clazz);
        }
        return null;
    }

    /**
     * Build full URL by concatenating base path, the given sub path and query parameters.
     *
     * @param path        The sub path
     * @param queryParams The query parameters
     * @return The full URL
     */
    public String buildUrl(String path, Map<String, String> queryParams) {
        final StringBuilder url = new StringBuilder();
        url.append(path);
        if (MapUtils.isNotEmpty(queryParams)) {
            String prefix = path.contains("?") ? "&" : "?";
            for (Map.Entry<String, String> entry : queryParams.entrySet()) {
                String key = entry.getKey();
                String value = entry.getValue();
                if (value != null) {
                    if (prefix != null) {
                        url.append(prefix);
                        prefix = null;
                    } else {
                        url.append("&");
                    }
                    url.append(escapeString(key)).append("=").append(escapeString(value));
                }
            }
        }
        return url.toString();
    }

    /**
     * Escape the given string to be used as URL query value.
     *
     * @param str String to be escaped
     * @return Escaped string
     */
    public String escapeString(String str) {
        try {
            return URLEncoder.encode(str, "utf8").replaceAll("\\+", "%20");
        } catch (UnsupportedEncodingException e) {
            return str;
        }
    }

    public static void main(String[] args) throws IOException {
        Client apiClient = new Client(false);
        String clientSecret="0a417ecce58c31b32364ce19ca8fcd15";
        String clientId="xclient";
        String url="http://47.100.197.8:8099/oauth/token?grant_type=client_credentials&client_id="+clientId+"&client_secret="+clientSecret;
        String accessToken = apiClient.post(url, (Headers) null, null, null);
        JsonObject object = new JsonParser().parse(accessToken).getAsJsonObject();
        String r = apiClient.get("http://47.100.197.8:8099/api/v1/app/getAllPublishBizSchema", Headers.of("access_token", object.get("access_token").getAsString()), null);
        System.out.println(r);
        JSON.parseObject(r);
        JsonObject obj= new JsonParser().parse(r).getAsJsonObject();
    }
}